/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.sober.impl;

import com.jspx.cache.JSCacheManager;
import com.jspx.sober.Criteria;
import com.jspx.sober.SoberEnv;
import com.jspx.sober.SoberFactory;
import com.jspx.sober.TableModels;
import com.jspx.sober.jdbc.JdbcOperations;
import com.jspx.sober.criteria.Order;
import com.jspx.sober.criteria.projection.Criterion;
import com.jspx.sober.criteria.projection.Projection;
import com.jspx.sober.dialect.Dialect;
import com.jspx.sober.util.DataMap;
import com.jspx.sober.util.JdbcUtil;
import com.jspx.sober.util.SoberUtil;
import com.jspx.utils.ArrayUtil;
import com.jspx.utils.ObjectUtil;
import com.jspx.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2007-1-8
 * Time: 10:28:37
 * hibernate 的 Criteria 方式扩展
 */
public class CriteriaImpl implements Criteria, Serializable {
    final private static Logger log = LoggerFactory.getLogger(CriteriaImpl.class);

    //当前处理类
    private Class criteriaClass;
    //数据源工厂
    private SoberFactory soberFactory = null;
    //表达式列表
    private List<CriterionEntry> criterionEntries = new ArrayList<CriterionEntry>();
    //排序列表
    private List<OrderEntry> orderEntries = new ArrayList<OrderEntry>();
    //分组列表
    private List<String> groupList = new ArrayList<String>();
    //基本的JDBC操作类
    private JdbcOperations jdbcOperations;
    //页数
    private Integer currentPage = 1;
    //行数
    private int totalCount = 10000;

    private Projection projection = null;

    public CriteriaImpl(Class criteriaClass, JdbcOperations jdbcOperations) {
        this.criteriaClass = criteriaClass;
        this.jdbcOperations = jdbcOperations;
        this.soberFactory = jdbcOperations.getSoberFactory();
        this.totalCount = jdbcOperations.getMaxRows();
    }

    public Class getCriteriaClass() {
        return criteriaClass;
    }

    public Criteria setCurrentPage(Integer currentPage) {
        this.currentPage = currentPage;
        return this;
    }

    public int getTotalCount() {
        return totalCount;
    }

    public Criteria setTotalCount(Integer totalCount) {
        if (totalCount>jdbcOperations.getMaxRows()) totalCount = jdbcOperations.getMaxRows();
        this.totalCount = totalCount;
        return this;
    }

    public Integer getCurrentPage() {
        return currentPage;
    }

    private Criteria add(Criteria criteriaInst, Criterion expression) {
        criterionEntries.add(new CriterionEntry(expression, criteriaInst));
        return this;
    }

    public Criteria add(Criterion criterion) {
        return add(this, criterion);
    }

    public Criteria addOrder(Order ordering) {
        orderEntries.add(new OrderEntry(ordering, this));
        return this;
    }

    public Criteria addGroup(String group) {
        groupList.add(group);
        return this;
    }

    /**
     * @param loadChild 是否载入映射
     * @return 载入单个对象
     */
    public Object objectUniqueResult(boolean loadChild) {
        setCurrentPage(1);
        setTotalCount(1);
        List list = list(loadChild);
        if (list==null||list.isEmpty()) return null;
        return list.get(0);
    }

    public boolean booleanUniqueResult() {
        return ObjectUtil.toBoolean(uniqueResult());
    }

    public int intUniqueResult() {
        return ObjectUtil.toInt(uniqueResult());
    }

    public long longUniqueResult() {
        return ObjectUtil.toLong(uniqueResult());
    }

    public float floatUniqueResult() {
        return ObjectUtil.toFloat(uniqueResult());
    }

    public double doubleUniqueResult() {
        return ObjectUtil.toDouble(uniqueResult());
    }

    /**
     * 返回单个对象
     * 是要统计的对象,sql 中包含 sum avg count 这些的才使用
     *
     * @return 返回单个对象
     */
    @SuppressWarnings("unchecked")
    public Object uniqueResult()
    {
        if (projection == null) return null;
        TableModels soberTable = soberFactory.getTableModels(criteriaClass, jdbcOperations);
        String databaseName = soberFactory.getDatabaseName();
        if (soberTable == null) return new ArrayList();
        StringBuilder termText = new StringBuilder();
        Object[] objectArray = null;
        for (int i = 0; i < criterionEntries.size(); i++) {
            CriterionEntry criterionEntry = criterionEntries.get(i);

            if (!SoberUtil.containsFields(soberTable,criterionEntry.criterion.getFields())) continue;
            String term = criterionEntry.criterion.toSqlString(soberTable,databaseName);
            termText.append(term);
            if (i != (criterionEntries.size() - 1)&& !StringUtil.isNULL(StringUtil.trim(term))) {
                termText.append(" AND ");
            }
            if (criterionEntry.criterion.getParameter(soberTable) != null) {
                objectArray = JdbcUtil.appendArray(objectArray, criterionEntry.criterion.getParameter(soberTable));
            }
        }

        StringBuilder groupText = new StringBuilder();
        for (int i = 0; i < groupList.size(); i++) {
            groupText.append(groupList.get(i));
            if (i != (groupList.size() - 1)) {
                groupText.append(",");
            }
        }

        StringBuilder orderText = new StringBuilder();
        for (int i = 0; i < orderEntries.size(); i++) {
            OrderEntry orderEntry = orderEntries.get(i);
            if (!SoberUtil.containsFields(soberTable,orderEntry.getOrder().getFields())) continue;
            orderText.append(orderEntry.order.toString());
            if (i != (orderEntries.size() - 1)) {
                orderText.append(",");
            }
        }

        Map<String, Object> valueMap = new HashMap<String, Object>();
        valueMap.put(Dialect.KEY_TABLE_NAME, soberTable.getTableName());
        valueMap.put(Dialect.KEY_FIELD_PROJECTION, projection.toSqlString(databaseName));
        valueMap.put(Dialect.KEY_TERM, termText.toString());
        valueMap.put(Dialect.KEY_FIELD_GROUPBY, groupText.toString());
        valueMap.put(Dialect.KEY_FIELD_ORDERBY, orderText.toString());

        Object result = null;

        //取出cache  begin
        StringBuilder cacheKey = new StringBuilder(soberTable.getTableName());
        String cacheName = null;
        if (soberFactory.isUseCache() && soberTable.isUseCache()) {
            StringBuilder termKey = new StringBuilder();
            termKey.append("_").append(termText.toString()).append("_p_").append(projection == null ? "" : projection.toSqlString(soberFactory.getDatabaseName())).append("_g_").append(groupText.toString()).append("_o_").append(orderText.toString());
            if (objectArray != null)
                for (Object po : objectArray) {
                    termKey.append(ObjectUtil.toString(po));
                }
            cacheKey.append(StringUtil.replace(StringUtil.replace(termKey.toString(), "?", "_"), " ", ""));
            cacheName = StringUtil.defaultIfEmpty(soberTable.getCacheName(), soberFactory.getCacheName());
            result = JSCacheManager.get(cacheName, SoberUtil.getListKey(soberTable.getEntity(), cacheKey.toString(), false));
            if (result != null) return result;
        }
        //取出cache  end

        PreparedStatement statement = null;
        ResultSet resultSet = null;
        Connection conn = null;

        Dialect dialect = soberFactory.getDialect();
        String sqlText = StringUtil.empty;
        try {
            conn = jdbcOperations.getConnection(SoberEnv.ReadOnly);
            sqlText = dialect.processTemplate(Dialect.SQL_CRITERIA_UNIQUERESULT, valueMap);
            jdbcOperations.debugPrint(sqlText);
            if (!dialect.supportsConcurReadOnly())
                statement = conn.prepareStatement(sqlText);
            else
                statement = conn.prepareStatement(sqlText, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

            statement.setMaxRows(1);
            if (objectArray != null) {
                for (int i = 0; i < objectArray.length; i++) {
                    jdbcOperations.debugPrint("SetPrepared[" + (i + 1) + "]=" + objectArray[i]);
                    dialect.setPreparedStatementValue(statement, i + 1, objectArray[i]);
                }
            }
            resultSet = statement.executeQuery();
            if (resultSet.getMetaData().getColumnCount() <= 1) {
                if (resultSet.next()) {
                    result = dialect.getResultSetValue(resultSet, 1);
                }
            } else if (resultSet.next()) {
                Map<String, Object> resultMap = new HashMap<String, Object>();
                for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
                    resultMap.put(resultSet.getMetaData().getColumnLabel(i), dialect.getResultSetValue(resultSet, i));
                }
                result = resultMap;
            }
        } catch (Exception e) {
            log.error("table:" + soberTable + " sql:" +sqlText, e);
            e.printStackTrace();
            soberFactory.closeConnection(conn, true);
        } finally {
            valueMap.clear();
            JdbcUtil.closeResultSet(resultSet);
            JdbcUtil.closeStatement(statement);
            jdbcOperations.closeConnection(conn);
        }

        //放入cache
        if (soberFactory.isUseCache() && soberTable.isUseCache()) {
            JSCacheManager.put(cacheName, SoberUtil.getListKey(soberTable.getEntity(), cacheKey.toString(), false), result);
        }
        return result;
    }

    /**
     * 快速删除,不删除映射对象
     *
     * @return boolean 是否成功
     */
    private int delete()
    {
        TableModels soberTable = soberFactory.getTableModels(criteriaClass, jdbcOperations);
        String databaseName = soberFactory.getDatabaseName();
        Dialect dialect = soberFactory.getDialect();
        StringBuilder termText = new StringBuilder();
        Object[] objectArray = null;
        for (int i = 0; i < criterionEntries.size(); i++) {
            CriterionEntry criterionEntry = criterionEntries.get(i);
            if (!SoberUtil.containsFields(soberTable,criterionEntry.criterion.getFields())) continue;
            String term = criterionEntry.criterion.toSqlString(soberTable,databaseName);
            termText.append(term);
            if (i != (criterionEntries.size() - 1)&& !StringUtil.isNULL(StringUtil.trim(term))) {
                termText.append(" AND ");
            }
            if (criterionEntry.criterion.getParameter(soberTable) != null) {
                objectArray = JdbcUtil.appendArray(objectArray, criterionEntry.criterion.getParameter(soberTable));
            }
        }
        Map<String, Object> valueMap = new HashMap<String, Object>();
        valueMap.put(Dialect.KEY_TABLE_NAME, soberTable.getTableName());
        valueMap.put(Dialect.KEY_FIELD_NAME, soberTable.getPrimary());
        valueMap.put(Dialect.KEY_PRIMARY_KEY, soberTable.getPrimary());
        valueMap.put(Dialect.KEY_TERM, termText.toString());
        return jdbcOperations.update(dialect.processTemplate(Dialect.SQL_CRITERIA_DELETE, valueMap), objectArray);

    }


    /**
     * 快速删除,不删除映射对象
     * @return boolean 是否成功
     */
    public int update(Map<String,Object> updateMap)
    {
        TableModels soberTable = soberFactory.getTableModels(criteriaClass, jdbcOperations);
        if (soberTable == null) {
            log.error("no fond sober Config :" + criteriaClass.getName());
            return -1;
        }
        Dialect dialect = soberFactory.getDialect();
        String databaseName = soberFactory.getDatabaseName();

        Object[] objectArray = null;
        String[] fieldList = null;
        for (String key:updateMap.keySet())
        {
            if (!soberTable.containsField(key)) continue;
            fieldList = ArrayUtil.add(fieldList,key);
            objectArray = JdbcUtil.appendArray(objectArray, updateMap.get(key));
        }

        StringBuilder termText = new StringBuilder();
        for (int i = 0; i < criterionEntries.size(); i++) {
            CriterionEntry criterionEntry = criterionEntries.get(i);
            if (!SoberUtil.containsFields(soberTable,criterionEntry.criterion.getFields())) continue;
            String term = criterionEntry.criterion.toSqlString(soberTable,databaseName);
            termText.append(term);
            if (i != (criterionEntries.size() - 1)&& !StringUtil.isNULL(StringUtil.trim(term))) {
                termText.append(" AND ");
            }
            if (criterionEntry.criterion.getParameter(soberTable) != null) {
                objectArray = JdbcUtil.appendArray(objectArray, criterionEntry.criterion.getParameter(soberTable));
            }
        }
        Map<String, Object> valueMap = new HashMap<String, Object>();
        valueMap.put(Dialect.KEY_TABLE_NAME, soberTable.getTableName());
        valueMap.put(Dialect.KEY_FIELD_LIST, fieldList);
        valueMap.put(Dialect.KEY_TERM, termText.toString());
        return jdbcOperations.update(dialect.processTemplate(Dialect.SQL_CRITERIA_UPDATE, valueMap),objectArray);
    }



    /**
     * 删除对象
     *
     * @param delChild 是否删除映射对象
     * @return boolean 是否成功
     */
    public int delete(boolean delChild) {
        int result = 0;
        if (delChild) {
            List list = list(false);
            try {
                for (Object o : list) {
                    if (o!=null) result = result + jdbcOperations.deleteNexus(o);
                }
            } catch (Exception e) {
                e.printStackTrace();
                return -2;
            }
        }
        if (delete()<0) return -1;
        return result + 1;
    }


    /**
     * 查询返回对象列表
     * 当多线程查询的时候很可能后边后边的cache还没放入，前边的查询已经取了执行了，这样cache在高并发下并不是理想的一次查询，
     * 而是同一个查询有可能会执行多次，到下一次检查到cache中有数据。所以如果是在多线程中使用加入 synchronized 来同步，效果更好
     *
     * @param loadChild 是否载入子对象
     * @return List 返回列表
     */
    @SuppressWarnings("unchecked")
    public List list(boolean loadChild)
    {
        TableModels soberTable = soberFactory.getTableModels(criteriaClass, jdbcOperations);
        if (soberTable == null) {
            log.error("no fond sober Config :" + criteriaClass.getName());
        }

        Dialect dialect = soberFactory.getDialect();
        String databaseName = soberFactory.getDatabaseName();
        StringBuilder termText = new StringBuilder();
        Object[] objectArray = null;
        for (int i = 0; i < criterionEntries.size(); i++) {
            CriterionEntry criterionEntry = criterionEntries.get(i);
            if (!SoberUtil.containsFields(soberTable,criterionEntry.criterion.getFields())) continue;
            String term = criterionEntry.criterion.toSqlString(soberTable,databaseName);
            termText.append(term);
            if (i != (criterionEntries.size() - 1)&& !StringUtil.isNULL(StringUtil.trim(term))) {
                termText.append(" AND ");
            }
            if (criterionEntry.criterion.getParameter(soberTable) != null) {
                objectArray = JdbcUtil.appendArray(objectArray, criterionEntry.criterion.getParameter(soberTable));
            }
        }
        StringBuilder groupText = new StringBuilder();
        for (int i = 0; i < groupList.size(); i++) {
            groupText.append(groupList.get(i));
            if (i != (groupList.size() - 1)) {
                groupText.append(",");
            }
        }
        StringBuilder orderText = new StringBuilder();
        for (int i = 0; i < orderEntries.size(); i++) {
            OrderEntry orderEntry = orderEntries.get(i);
            if (!SoberUtil.containsFields(soberTable,orderEntry.getOrder().getFields())) continue;
            orderText.append(orderEntry.order.toString());
            if (i != (orderEntries.size() - 1)) {
                orderText.append(",");
            }
        }
        if (orderText.toString().endsWith(",")) orderText.setLength(orderText.length()-1);
        if (currentPage <= 0) currentPage = 1;
        int iEnd = currentPage * totalCount;
        if (iEnd < 0) iEnd = 1;
        int iBegin = iEnd - totalCount;
        if (iBegin < 0) iBegin = 1;

        Map<String, Object> valueMap = new HashMap<String, Object>();
        valueMap.put(Dialect.KEY_TABLE_NAME, soberTable.getTableName());
        valueMap.put(Dialect.KEY_TERM, termText.toString());
        valueMap.put(Dialect.KEY_FIELD_GROUPBY, groupText.toString());
        valueMap.put(Dialect.KEY_FIELD_ORDERBY, orderText.toString());
        valueMap.put(Dialect.SQL_RESULT_BEGIN_ROW, iBegin);
        valueMap.put(Dialect.SQL_RESULT_END_ROW, iEnd);

        List resultList = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        Connection conn = null;
        String sqlText = StringUtil.empty;
        //取出cache  begin
        String cacheKey = null;
        String cacheName = null;

        if (soberFactory.isUseCache() && soberTable.isUseCache()) {
            StringBuilder termKey = new StringBuilder();
            for (int i=0; i<criterionEntries.size();i++)
            {
                CriterionEntry criterionEntry = criterionEntries.get(i);
                if (!SoberUtil.containsFields(soberTable,criterionEntry.criterion.getFields())) continue;
                termKey.append(criterionEntry.criterion.termString());
                if (i!=criterionEntries.size()-1) termKey.append("_");
            }
            if (termKey.toString().endsWith("_")) termKey.setLength(termKey.length()-1);
            if (StringUtil.hasLength(groupText.toString())) termKey.append("_g_").append(groupText.toString());
            if (StringUtil.hasLength(orderText.toString())) termKey.append("_o_").append(orderText.toString());
            if (termKey.toString().endsWith("_")) termKey.setLength(termKey.length()-1);
            termKey.append("_L_").append(iBegin).append("-").append(iEnd);
            cacheKey = SoberUtil.getListKey(soberTable.getEntity(),StringUtil.replace(termKey.toString(),"=", "_"), loadChild);
            cacheName = StringUtil.defaultIfEmpty(soberTable.getCacheName(), soberFactory.getCacheName());
            resultList = (List) JSCacheManager.get(cacheName, cacheKey);
            if (resultList != null && !resultList.isEmpty()) {
                return resultList;
            }
        }
        //取出cache  end

        try
        {
            conn = jdbcOperations.getConnection(SoberEnv.ReadOnly);
            sqlText = dialect.processTemplate(Dialect.SQL_CRITERIA_QUERY, valueMap);
            sqlText = dialect.getLimitString(sqlText, iBegin, iEnd, soberTable);

            jdbcOperations.debugPrint(sqlText);
            //结果集的游标可以上下移动，当数据库变化时，当前结果集不变
            if (!dialect.supportsConcurReadOnly())
                statement = conn.prepareStatement(sqlText);
            else
                statement = conn.prepareStatement(sqlText, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

            statement.setFetchSize(soberFactory.getFetchSize());
            statement.setMaxRows(iEnd);
            if (objectArray != null) {
                for (int i = 0; i < objectArray.length; i++) {
                    jdbcOperations.debugPrint("setPrepared[" + (i + 1) + "]=" + objectArray[i]);
                    dialect.setPreparedStatementValue(statement, i + 1, objectArray[i]);
                }
            }
            resultSet = statement.executeQuery();
            if (resultSet==null) return resultList;
            if (!dialect.supportsLimit()) {
                resultSet.absolute(iBegin);
            }

            resultList = new ArrayList();
            while (resultSet.next()) {
                Object tempObj = jdbcOperations.loadColumnsValue(soberTable, resultSet);
                jdbcOperations.calcUnique(soberTable, tempObj);
                resultList.add(tempObj);
                if (resultList.size() >= totalCount) break;
            }
            if (loadChild) {
                jdbcOperations.loadNexusList(criteriaClass, resultList);
            }
        } catch (Exception e) {
            log.info(sqlText, e);
            soberFactory.closeConnection(conn, true);
            e.printStackTrace();
        } finally {
            JdbcUtil.closeResultSet(resultSet);
            JdbcUtil.closeStatement(statement);
            jdbcOperations.closeConnection(conn);
            valueMap.clear();
        }
        //放入cache
        if (soberFactory.isUseCache() && soberTable.isUseCache() && !resultList.isEmpty() && StringUtil.hasLength(cacheName)) {
            if (!JSCacheManager.put(cacheName, cacheKey, resultList)) {
                log.error(cacheName + " put cache key " + cacheKey);
            }
        }
        return resultList;
    }

    public List groupList()
    {
        TableModels soberTable = soberFactory.getTableModels(criteriaClass, jdbcOperations);
        if (soberTable == null) {
            log.error("no fond sober Config :" + criteriaClass.getName());
        }
        Dialect dialect = soberFactory.getDialect();
        String databaseName = soberFactory.getDatabaseName();
        StringBuilder termText = new StringBuilder();
        Object[] objectArray = null;
        for (int i = 0; i < criterionEntries.size(); i++) {
            CriterionEntry criterionEntry = criterionEntries.get(i);
            if (!SoberUtil.containsFields(soberTable,criterionEntry.criterion.getFields())) continue;
            String term = criterionEntry.criterion.toSqlString(soberTable,databaseName);
            termText.append(term);
            if (i != (criterionEntries.size() - 1)&& !StringUtil.isNULL(StringUtil.trim(term))) {
                termText.append(" AND ");
            }
            if (criterionEntry.criterion.getParameter(soberTable) != null) {
                objectArray = JdbcUtil.appendArray(objectArray, criterionEntry.criterion.getParameter(soberTable));
            }
        }
        StringBuilder groupText = new StringBuilder();
        for (int i = 0; i < groupList.size(); i++) {
            groupText.append(groupList.get(i));
            if (i != (groupList.size() - 1)) {
                groupText.append(",");
            }
        }
        StringBuilder orderText = new StringBuilder();
        for (int i = 0; i < orderEntries.size(); i++) {
            OrderEntry orderEntry = orderEntries.get(i);
            if (!SoberUtil.containsFields(soberTable,orderEntry.getOrder().getFields())) continue;
            orderText.append(orderEntry.order.toString());
            if (i != (orderEntries.size() - 1)) {
                orderText.append(",");
            }
        }
        if (orderText.toString().endsWith(",")) orderText.setLength(orderText.length()-1);
        if (currentPage <= 0) currentPage = 1;
        int iEnd = currentPage * totalCount;
        if (iEnd < 0) iEnd = 1;
        int iBegin = iEnd - totalCount;
        if (iBegin < 0) iBegin = 1;

        Map<String, Object> valueMap = new HashMap<String, Object>();
        valueMap.put(Dialect.KEY_TABLE_NAME, soberTable.getTableName());
        valueMap.put(Dialect.KEY_TERM, termText.toString());
        valueMap.put(Dialect.KEY_FIELD_GROUPBY, groupText.toString());
        valueMap.put(Dialect.KEY_FIELD_ORDERBY, orderText.toString());
        valueMap.put(Dialect.SQL_RESULT_BEGIN_ROW, iBegin);
        valueMap.put(Dialect.SQL_RESULT_END_ROW, iEnd);

        List resultList = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        Connection conn = null;
        String sqlText = StringUtil.empty;
        //取出cache  begin
        String cacheKey = null;
        String cacheName = null;

        if (soberFactory.isUseCache() && soberTable.isUseCache()) {
            StringBuilder termKey = new StringBuilder();

            for (int i=0; i<criterionEntries.size();i++)
            {
                CriterionEntry criterionEntry = criterionEntries.get(i);
                if (!SoberUtil.containsFields(soberTable,criterionEntry.criterion.getFields())) continue;
                termKey.append(criterionEntry.criterion.termString());
                if (i!=criterionEntries.size()-1) termKey.append("_");
            }
            if (termKey.toString().endsWith("_")) termKey.setLength(termKey.length()-1);
            if (StringUtil.hasLength(groupText.toString())) termKey.append("_g_").append(groupText.toString());
            if (StringUtil.hasLength(orderText.toString())) termKey.append("_o_").append(orderText.toString());
            if (termKey.toString().endsWith("_")) termKey.setLength(termKey.length()-1);
            termKey.append("_L_").append(iBegin).append("-").append(iEnd);

            cacheKey = SoberUtil.getGroupListKey(soberTable.getEntity(), termKey.toString());
            cacheName = StringUtil.defaultIfEmpty(soberTable.getCacheName(), soberFactory.getCacheName());

            resultList = (List) JSCacheManager.get(cacheName, cacheKey);
            if (resultList != null && !resultList.isEmpty()) {
                return resultList;
            }
        }
        //取出cache  end

        try
        {
            conn = jdbcOperations.getConnection(SoberEnv.ReadOnly);
            sqlText = dialect.processTemplate(Dialect.SQL_CRITERIA_GROUP_QUERY, valueMap);
            sqlText = dialect.getLimitString(sqlText, iBegin, iEnd, soberTable);

            jdbcOperations.debugPrint(sqlText);
            //结果集的游标可以上下移动，当数据库变化时，当前结果集不变
            if (!dialect.supportsConcurReadOnly())
                statement = conn.prepareStatement(sqlText);
            else
                statement = conn.prepareStatement(sqlText, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            if (statement.getFetchSize()!=soberFactory.getFetchSize())
                    statement.setFetchSize(soberFactory.getFetchSize());
            statement.setMaxRows(iEnd);
            if (objectArray != null) {
                for (int i = 0; i < objectArray.length; i++) {
                    jdbcOperations.debugPrint("SetPrepared[" + (i + 1) + "]=" + objectArray[i]);
                    dialect.setPreparedStatementValue(statement, i + 1, objectArray[i]);
                }
            }
            resultSet = statement.executeQuery();
            if (!dialect.supportsLimit()) {
                resultSet.absolute(iBegin);
            }
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            int numColumns = resultSetMetaData.getColumnCount();
            resultList = new ArrayList();
            while (resultSet.next()) {
                Map<String, Object> resultMap = new DataMap<String, Object>();
                for (int c = 1; c <= numColumns; c++) {
                    String colName = resultSetMetaData.getColumnLabel(c);
                    resultMap.put(colName, dialect.getResultSetValue(resultSet, c));
                }
                resultList.add(resultMap);
                if (resultList.size() >= totalCount) break;
            }
        } catch (Exception e) {
            log.info(sqlText, e);
            soberFactory.closeConnection(conn, true);
            e.printStackTrace();
        } finally {
            JdbcUtil.closeResultSet(resultSet);
            JdbcUtil.closeStatement(statement);
            jdbcOperations.closeConnection(conn);
            valueMap.clear();
        }

        //放入cache
        if (soberFactory.isUseCache() && soberTable.isUseCache() && !resultList.isEmpty() && StringUtil.hasLength(cacheName)) {
            if (!JSCacheManager.put(cacheName, cacheKey, resultList)) {
                log.error(cacheName + " put cache key " + cacheKey);
            }
        }
        return resultList;
    }

    public Criteria setProjection(Projection projection)
    {
        this.projection = projection;
        return this;
    }

    class CriterionEntry implements Serializable {
        private Criterion criterion;
        private Criteria criteria;

        private CriterionEntry(Criterion criterion, Criteria criteria) {
            this.criteria = criteria;
            this.criterion = criterion;
        }

        public Criterion getCriterion() {
            return criterion;
        }

        public Criteria getCriteria() {
            return criteria;
        }

        public String toString() {
            return criterion.toString();
        }
    }

    class OrderEntry implements Serializable {
        private Order order;
        private Criteria criteria;

        private OrderEntry(Order order, Criteria criteria) {
            this.criteria = criteria;
            this.order = order;
        }

        public Order getOrder() {
            return order;
        }

        public Criteria getCriteria() {
            return criteria;
        }

        public String toString() {
            return order.toString();
        }
    }
}